/*
 * All rights Reserved, Designed By Rylynn
 * @ClassName:  Graph
 * @Description:
 * @author: rylynn
 * @date:   2021/7/7
 *
 */

package org.etnetwork.core;

import com.google.common.base.Preconditions;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class Graph extends AbstractGraph {
    private final List<Vertex> vertices;
    private List<Edge> edges;
    private Long maxVid;

    public Graph() {
        this.vertices = new ArrayList<>();
        this.edges = new ArrayList<>();
        this.maxVid = 0L;
    }

//    public static Graph buildFromEdgeList(List<String> source, List<String> target) {
//        Graph g = new Graph();
//        if (source.size() != target.size()) {
//            throw new IllegalArgumentException(String.format("The size of the source nodes list should be the same as the target nodes list, but get sources: %d, target: %d",
//                            source.size(),
//                            target.size()));
//        }
//        Iterator<String> sourceIter;
//        Iterator<String> targetIter;
//        sourceIter = source.listIterator();
//        targetIter = target.listIterator();
//
//        while (sourceIter.hasNext() && targetIter.hasNext()) {
//            String s = sourceIter.next();
//            String t = targetIter.next();
//
//        }
//        return new Graph();
//    }

    public static Graph buildFromAdj() {
        return new Graph();
    }

    @Override
    public Graph addVertex(Vertex vertex) {
        Long vertexId = vertex.getVid();
        for (Vertex v : vertices) {
            if (v.getVid().equals(vertexId)) {
                vertices.remove(v);
                break;
            }
        }
        this.vertices.add(vertex);
        return this;
    }

    @Override
    public Graph addEdge(Edge edge) {
        Optional<Vertex> hasSource = vertices.stream().filter(vertex -> edge.getSource().equals(vertex.getVid())).findFirst();
        if (!hasSource.isPresent()) {
            Vertex vertex = new Vertex(edge.getSource());
            if (vertex.getVid() > this.maxVid) {
                this.maxVid = vertex.getVid();
            }
            this.vertices.add(vertex);
        }

        Optional<Vertex> hasTarget = vertices.stream().filter(vertex -> edge.getTarget().equals(vertex.getVid())).findFirst();
        if (!hasTarget.isPresent()) {
            Vertex vertex = new Vertex(edge.getTarget());
            if (vertex.getVid() > this.maxVid) {
                this.maxVid = vertex.getVid();
            }
            this.vertices.add(vertex);
        }

        this.edges.add(edge);
        return this;
    }

    @Override
    public Graph addVertices(Vertex... vertices) {
        this.vertices.forEach(this::addVertex);
        return this;
    }

    @Override
    public Graph addEdges(Edge... edges) {
        for (Edge edge : edges) {
            Long sourceId = edge.getSource();
            Long targetId = edge.getTarget();
        }
        this.edges.forEach(this::addEdge);
        return this;
    }

    @Override
    public Graph deleteVertex(Vertex vertex) {
        this.vertices.remove(vertex);
        return this;
    }

    @Override
    public Graph deleteEdge(Edge edge) {
        this.edges.remove(edge);
        return this;
    }

    @Override
    public Graph deleteEdgeFrom(Long sourceId) {
        List<Edge> deleteEdges = this.edges.stream().filter(edge -> sourceId.equals(edge.getSource())).collect(Collectors.toList());
        this.edges.removeAll(deleteEdges);
        return this;
    }

    @Override
    public Graph deleteEdgeTo(Long targetId) {
        List<Edge> deleteEdges = this.edges.stream().filter(edge -> targetId.equals(edge.getTarget())).collect(Collectors.toList());
        this.edges.removeAll(deleteEdges);
        return this;
    }

    @Override
    public Graph deleteVertices(Vertex... vertices) {
        Arrays.stream(vertices).forEach(this.vertices::remove);
        return this;
    }

    @Override
    public Graph deleteEdges(Edge... edges) {
        Arrays.stream(edges).forEach(this.edges::remove);
        return this;
    }

    @Override
    public Optional<Vertex> getVertex(Long vid) {
        return this.vertices.stream().filter(o -> o.getVid().equals(vid)).findFirst();
    }

    @Override
    public Optional<Vertex> getEdge(Long source, Long target) {
        return Optional.empty();
    }

    public List<Vertex> getVertices() {
        return vertices;
    }

    public List<Edge> getEdges() {
        return edges;
    }

    public void setEdges(List<Edge> edges) {
        this.edges = edges;
    }

    @Override
    public List<Vertex> getVertices(Map<String, Object> properties) {
        Predicate<Vertex> containsProperties = vertex -> {
            if (properties == vertex.getProperties()) {
                return true;
            }
            return true;
        };
        return this.vertices.stream().filter(containsProperties).collect(Collectors.toList());
    }

    @Override
    public List<Edge> getEdges(Map<String, Object> properties) {
        return null;
    }

    @Override
    public Optional<Vertex> hasVertex(Long vertexId) {
        return this.vertices.stream().filter(vertex -> vertexId.equals(vertex.getVid())).findFirst();

    }

    @Override
    public Optional<Edge> hasEdge(Long sourceId, Long targetId) {
        return this.edges.stream().filter(e -> sourceId.equals(e.getSource()) && targetId.equals(e.getTarget())).findFirst();
    }

    @Override
    public Integer getEdgesNumber() {
        return this.edges.size();
    }

    @Override
    public Integer getVerticesNumber() {
        return this.vertices.size();
    }

    @Override
    public String toString() {
        return "Graph{" +
                "vertices: " + vertices.size() +
                ", edges: " + edges.size() +
                '}';
    }
}
